// 版权所有2022 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types

// 类型的结构类型计算的实现。

// TODO:我们只希望依赖结构类型的类型2计算，
// 但我们只能在下次更改导出格式并导出
// 结构类型信息以及每个约束类型时这样做，因为编译器将
// 类型直接导入类型1格式。

// A term描述基本类型集：
// 
// T类型的term{false，T}集
// term{true，T}类型集与底层类型T 
// term{空集（我们专门检查typ==nil）
type term struct {
	tilde bool
	typ   *Type
}

// StructuralType返回接口的结构类型，如果没有
// 结构类型，则为零。
func (t *Type) StructuralType() *Type {
	sts, _ := specificTypes(t)
	var su *Type
	for _, st := range sts {
		u := st.typ.Underlying()
		if su != nil {
			u = match(su, u)
			if u == nil {
				return nil
			}
		}
		// su==nil | | match（su，u）！=nil 
		su = u
	}
	return su
}

// 如果x和y相同，则match返回x。
// 如果x和y是相同的通道，但它们的方向
// 并且其中一个是不受限制的，则match返回通道
// 和受限制的方向。
// 在所有其他情况下，match返回零。
// x和y被假定为基础类型，因此不是命名类型。
func match(x, y *Type) *Type {
	if IdenticalStrict(x, y) {
		return x
	}

	if x.IsChan() && y.IsChan() && IdenticalStrict(x.Elem(), y.Elem()) {
		// 我们只有方向不同的频道。
		// 如果有不受限制的频道，请选择受限制的频道。
		// 如果两个方向相同，则返回x（任意一个都可以）。
		switch {
		case x.ChanDir().CanSend() && x.ChanDir().CanRecv():
			return y
		case y.ChanDir().CanSend() && y.ChanDir().CanRecv():
			return x
		}
	}
	return nil
}

// specificTypes返回接口类型的特定类型列表，如果没有，则返回nil。它还返回一个标志，指示对于空项列表
// 结果，它是表示空集，还是表示所有类型的无限集（在
// 两种情况下，都没有特定类型）。
func specificTypes(t *Type) (list []term, inf bool) {
	t.wantEtype(TINTER)

	// 在处理任何类型元素之前，我们有无限的术语列表
	// （或者如果没有类型元素）。
	inf = true
	for _, m := range t.Methods().Slice() {
		var r2 []term
		inf2 := false

		switch {
		case m.IsMethod():
			inf2 = true

		case m.Type.IsUnion():
			nt := m.Type.NumTerms()
			for i := 0; i < nt; i++ {
				t, tilde := m.Type.Term(i)
				if t.IsInterface() {
					r3, r3inf := specificTypes(t)
					if r3inf {
						// 具有无限类型集的并集是
						// 无限的，所以跳过剩余的项。
						r2 = nil
						inf2 = true
						break
					}
					// 将r3的元素添加到r2中。
					for _, r3e := range r3 {
						r2 = insertType(r2, r3e)
					}
				} else {
					r2 = insertType(r2, term{tilde, t})
				}
			}

		case m.Type.IsInterface():
			r2, inf2 = specificTypes(m.Type)

		default:
			// m.Type是一个非接口类型，所以r2只是一个
			// one-element list，inf2为false。
			r2 = []term{{false, m.Type}}
		}

		if inf2 {
			// 如果当前类型元素有无限个类型，
			// 它与r的交集就是r，所以跳过这个类型元素。
			continue
		}

		if inf {
			// 如果r是无限的，那么r和r2的交集就是r2。
			list = r2
			inf = false
			continue
		}

		// r和r2是有限的，所以r和r2相交。
		var r3 []term
		for _, re := range list {
			for _, r2e := range r2 {
				if tm := intersect(re, r2e); tm.typ != nil {
					r3 = append(r3, tm)
				}
			}
		}
		list = r3
	}
	return
}

// insertType将t添加到返回的列表中，如果它还不在列表中。
func insertType(list []term, tm term) []term {
	for i, elt := range list {
		if new := union(elt, tm); new.typ != nil {
			// 用英语教学和新英语教学的结合来取代现有英语教学。
			list[i] = new
			return list
		}
	}
	return append(list, tm)
}

// 如果x和y是不相交的，则返回类型为零的术语（这意味着联合应
// 包括这两种类型）。如果x和y不是不相交的，则返回单个类型，即
// x和y的并集。
func union(x, y term) term {
	if disjoint(x, y) {
		return term{false, nil}
	}
	if x.tilde || !y.tilde {
		return x
	}
	return y
}

// intersect返回交集x∩ y、 
func intersect(x, y term) term {
	if disjoint(x, y) {
		return term{false, nil}
	}
	if !x.tilde || y.tilde {
		return x
	}
	return y
}

// 不相交的报告是否为x∩ y==∅. 
func disjoint(x, y term) bool {
	ux := x.typ
	if y.tilde {
		ux = ux.Underlying()
	}
	uy := y.typ
	if x.tilde {
		uy = uy.Underlying()
	}
	return !IdenticalStrict(ux, uy)
}
